#include "global_illumination.h"

#include "raytracing/local_illumination.h"
#include "material/light_material.h"
#include "material/object_material.h"
#include "material/color_material.h"
#include "material/texture_material.h"
#include "light/light.h"
#include "math/ray.h"
#include "math/vector.h"
#include "common/scene.h"
#include "object/object.h"
#include "kd_tree.h"

#include <cstdlib>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>

//using namespace boost::random;

namespace Sirat {

boost::mt19937 mersenne_twister;
boost::uniform_real<> real_dist(-1.0,1.0);
boost::variate_generator<boost::mt19937&, boost::uniform_real<> > random(mersenne_twister, real_dist);
	
namespace Photon_Mapping {

Common::Color Global_Illumination::indirect(Object::Object* _object,const Math::Ray& _ray,const Math::Vector& _point,Common::Scene* _scene,const unsigned int _n) {
	// Using Mersenne Twister pseudo random generator
	
	Common::Color color;
	Math::Vector normal = _object->get_normal(_point);

	for(unsigned int i = 0; i < _n ; i++) {
		Math::Vector direction;
		do {
			direction = Math::Vector(random(),
							   random(),
								random()
							  );
		} while(normal.dot_product(direction) < 0.0 && (direction.length() > 1.0));
		direction = direction.normalize();
		Math::Ray indirect(_point+normal*0.001,direction);
		
		tuple<Object::Object*,Math::Vector> result = _scene->nearest_object(indirect);
		
		Object::Object* object_ = result.get<0>();
		if(object_ != 0) {
			Math::Vector point_ = result.get<1>();
			double weight = std::max(0.0,std::min(1.0,(-direction).dot_product(object_->get_normal(point_))));
			color = color + weight*Ray_Tracing::Local_Illumination::calculate_color(object_,indirect,point_,_scene)
					*static_cast<Material::Color_Material*>(_object->get_material())->get_diffuse();
		}	
	}	
	return color*(1.0/(double)_n);
}

}

}
